#pragma once

namespace shijie 
{
	namespace utility
	{
		template<typename T> 
		class SharedPtr
		{
		template <typename T> friend class WeakPtr;
		public:
			SharedPtr() : m_data(nullptr), m_count(nullptr){}
			SharedPtr(T *data) : m_data(data) {
				if (data != nullptr) {
					m_count = new int(1);
				}
			}
			
			SharedPtr(const SharedPtr<T>& other) : m_data(other.m_data), m_count(other.m_count) {
				if (m_data != nullptr) {
					(*m_count)++;
				}
			}

			SharedPtr(SharedPtr<T>&& other) noexcept : m_data(other.m_data), m_count(other.m_count) {
				other.m_data = nullptr;
				other.m_count = nullptr;
			} 

			~SharedPtr() {
				if (m_data != nullptr) {
					(*m_count)--;
					if (*m_count <= 0) {
						delete m_data;
						m_data = nullptr;
						delete m_count;
						m_count = nullptr;
					}
				}
			}

			T * get() const {
				return m_data;
			}

			void reset(T *data = nullptr) {
				if (m_data == data) {
					return;
				}

				if (m_data == nullptr) {
					if (data != nullptr) {
						m_data = data;
						m_count = new int(1);
					}
					return;
				}
				(*m_count)--;
				if (*m_count <= 0) {
					delete m_data;
					m_data = nullptr;
					delete m_count;
					m_count = nullptr;
				}

				m_data = data;
				if (data != nullptr) {
					m_count = new int(1);
				}
			} 

			bool unique() const {
				if (m_data == nullptr) {
					return false;
				}
				return *m_count == 1;
			}

			int use_count() const {
				if (m_data == nullptr) {
					return 0;
				}
				return *m_count;
			}

			void swap(SharedPtr<T>& other) {
				auto data = other.m_data;
				auto count = other.m_count;
				other.m_data = m_data;
				other.m_count = m_count;
				m_data = data;
				m_count = count;
			}

			T *operator -> () const {
				return m_data;
			}  

			T & operator *() const {
				return *m_data;
			}

			explicit operator bool() const noexcept {
				return m_data != nullptr;
			}

			SharedPtr & operator = (const SharedPtr<T> &other) {
				if (this == &other) {
					return *this;
				}
				
				// 减少原有引用计数
				if (m_data != nullptr) {
					(*m_count)--;
					if (*m_count <= 0) {
						delete m_data;
						delete m_count;
						m_data = nullptr;
						m_count = nullptr;
					}
				}
				
				// 指向新对象并增加引用计数
				m_data = other.m_data;
				m_count = other.m_count;
				if (m_data != nullptr) {
					(*m_count)++;
				}
				
				return *this;
			}

			SharedPtr & operator = (SharedPtr<T> &&other) noexcept {
				if (this == &other) {
					return *this;
				}
				
				// 减少原有引用计数
				if (m_data != nullptr) {
					(*m_count)--;
					if (*m_count <= 0) {
						delete m_data;
						delete m_count;
						m_data = nullptr;
						m_count = nullptr;
					}
				}
				
				// 转移所有权
				m_data = other.m_data;
				m_count = other.m_count;
				other.m_data = nullptr;
				other.m_count = nullptr;
				
				return *this;
			}

		private:
			T *m_data;
			int *m_count;
		};
	}
}